package com.yanfan.energybilling.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yanfan.common.core.mq.DeviceReport;
import com.yanfan.common.core.thingsModel.ThingsModelSimpleItem;
import com.yanfan.common.utils.uuid.IdUtils;
import com.yanfan.energybilling.domain.DeviceThingsEnergy;
import com.yanfan.energybilling.domain.PriceScheme;
import com.yanfan.energybilling.domain.PriceSchemeTime;
import com.yanfan.energybilling.domain.TDEnergyDeductionRecord;
import com.yanfan.energybilling.service.DeviceThingsEnergyService;
import com.yanfan.energybilling.service.IEnergyDeductionRecordService;
import com.yanfan.energybilling.service.IPriceSchemeService;
import com.yanfan.energybilling.service.ITDEnergyDeductionRecordService;
import com.yanfan.iot.domain.Device;
import com.yanfan.iot.domain.ThingsModel;
import com.yanfan.iot.service.IDeviceService;
import com.yanfan.iot.service.IThingsModelService;
import com.yanfan.mq.service.IMqttMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Component
public class EnergyDeductionRecordServiceImpl implements IEnergyDeductionRecordService, IMqttMessageService {
    @Resource
    private IDeviceService deviceService;
    @Resource
    private IPriceSchemeService priceSchemeService;
    @Resource
    private DeviceThingsEnergyService deviceThingsEnergyService;
    @Resource
    private ITDEnergyDeductionRecordService energyDeductionRecordService;
    @Resource
    private IThingsModelService thingsModelService;

    @Override
    public void message(DeviceReport message, String topicName) {
        Device dbDevice = deviceService.selectDeviceBySerialNumber(message.getSerialNumber());
        if (Objects.isNull(dbDevice)) return;
//        log.error("message:{}", JSONObject.toJSONString(message));
        List<ThingsModelSimpleItem> thingsModelSimpleItems = message.getThingsModelSimpleItem();
        List<String> thingModelIds = thingsModelSimpleItems.stream().map(ThingsModelSimpleItem::getId).collect(Collectors.toList());

        log.error("List<ThingsModelSimpleItem> ：{} " , JSONObject.toJSONString(thingsModelSimpleItems));
        log.error("thingModelIds ：{} " , JSONObject.toJSONString(thingModelIds));

        List<DeviceThingsEnergy> dbDeviceThingEnergyList = deviceThingsEnergyService.selectDeviceThingsEnergyByDeviceIdAndAndThingModelIds(dbDevice.getDeviceId(), thingModelIds);

        for (DeviceThingsEnergy dbDeviceThingsEnergy : dbDeviceThingEnergyList) {
            ThingsModel thingsModel = thingsModelService.selectThingsModelByModelId(dbDeviceThingsEnergy.getThingsModelId());
            String identifier = thingsModel.getIdentifier();
            List<ThingsModelSimpleItem> collect = thingsModelSimpleItems.stream().filter(thingsModelSimpleItem -> thingsModelSimpleItem.getId().equals(identifier)).collect(Collectors.toList());
            ThingsModelSimpleItem thingsModelSimpleItem = collect.get(0);
            log.error("能耗中对比值thingsModelSimpleItem ：{} " , JSONObject.toJSONString(thingsModelSimpleItem));
            this.meteredDeduction(dbDevice.getDeviceId(), thingsModelSimpleItem, thingsModel.getModelId(), dbDeviceThingsEnergy);

        }

        log.error("**************************************************************************");

    }


    /**
     * 计量扣费
     *
     * @param deviceId             设备id
     * @param dbDeviceThingsEnergy
     * @return 结果
     */
    public void meteredDeduction(Long deviceId, ThingsModelSimpleItem thingsModelSimpleItem, Long thingsModelId, DeviceThingsEnergy dbDeviceThingsEnergy) {
        Device dbDevice = deviceService.selectDeviceByDeviceId(deviceId);
        PriceScheme dbPriceScheme = priceSchemeService.selectPriceSchemeByDeviceIdAnd(deviceId, thingsModelId);
        String chargeType = dbPriceScheme.getChargeType();
        List<PriceSchemeTime> timeList = dbPriceScheme.getTimeList();
        JSONArray priceList = new JSONArray();

        //上次的记录
        TDEnergyDeductionRecord latestRecord = energyDeductionRecordService.selectLatestRecord(dbDevice.getSerialNumber(), thingsModelSimpleItem.getId());

        log.error("SN码：{}" , dbDevice.getSerialNumber());
        if (!Objects.isNull(latestRecord)) {
            //第一次不扣费
            log.error("上次的记录 ：{}" , JSONObject.toJSONString(latestRecord));
        }
        switch (chargeType) {
            case "1":
                //计量
                // 因为时间段的价格类型是从1开始的，所以加一个空后边好操作
                priceList.add(null);
                priceList.add(JSONObject.of("price", dbPriceScheme.getMeterPrice1(), "servicePrice", dbPriceScheme.getServicePrice1()));
                priceList.add(JSONObject.of("price", dbPriceScheme.getMeterPrice2(), "servicePrice", dbPriceScheme.getServicePrice2()));
                priceList.add(JSONObject.of("price", dbPriceScheme.getMeterPrice3(), "servicePrice", dbPriceScheme.getServicePrice3()));
                priceList.add(JSONObject.of("price", dbPriceScheme.getMeterPrice4(), "servicePrice", dbPriceScheme.getServicePrice4()));

                if (Objects.isNull(latestRecord)) {
                    //第一次不扣费
                    this.insertRecord(dbDevice, dbDeviceThingsEnergy, thingsModelSimpleItem, thingsModelSimpleItem.getId(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, dbDeviceThingsEnergy.getWorkshopId(), dbDeviceThingsEnergy.getBuildingName(), 0);
                } else {
                    JSONObject feeObject = this.calculateFeeValue(timeList, priceList, thingsModelSimpleItem, latestRecord);

                    BigDecimal price = feeObject.getBigDecimal("price");
                    BigDecimal servicePrice = feeObject.getBigDecimal("servicePrice");
                    BigDecimal totalFee = feeObject.getBigDecimal("totalFee");
                    BigDecimal serviceTotalFee = feeObject.getBigDecimal("serviceTotalFee");
                    BigDecimal differenceValue = feeObject.getBigDecimal("differenceValue");
                    if (!(differenceValue.signum() > 0)) {
                        // signum() 返回：1(正数), 0(零), -1(负数)
                        log.error("differenceValue 小于等于: 0,数据为：{}: " , differenceValue);
                        break;
                    }
                    //费用类型
                    Integer type = feeObject.getInteger("type");
                    this.insertRecord(dbDevice, dbDeviceThingsEnergy, thingsModelSimpleItem, thingsModelSimpleItem.getId(), price, servicePrice, totalFee, serviceTotalFee, differenceValue, dbDeviceThingsEnergy.getWorkshopId(), dbDeviceThingsEnergy.getBuildingName(), type);
                }

                break;
            case "2":
                //计时
                // 因为时间段的价格类型是从1开始的，所以加一个空后边好操作
                priceList.add(null);
                priceList.add(JSONObject.of("price", dbPriceScheme.getTimePrice1(), "servicePrice", dbPriceScheme.getServicePrice1()));
                priceList.add(JSONObject.of("price", dbPriceScheme.getTimePrice2(), "servicePrice", dbPriceScheme.getServicePrice2()));
                priceList.add(JSONObject.of("price", dbPriceScheme.getTimePrice3(), "servicePrice", dbPriceScheme.getServicePrice3()));
                priceList.add(JSONObject.of("price", dbPriceScheme.getTimePrice4(), "servicePrice", dbPriceScheme.getServicePrice4()));

                if (Objects.isNull(latestRecord)) {
                    //第一次不扣费
                    this.insertRecord(dbDevice, dbDeviceThingsEnergy, thingsModelSimpleItem, thingsModelSimpleItem.getId(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, dbDeviceThingsEnergy.getWorkshopId(), dbDeviceThingsEnergy.getBuildingName(), 0);
                } else {
                    JSONObject feeObject = this.calculateFeeTime(timeList, priceList, thingsModelSimpleItem, latestRecord);
                    BigDecimal price = feeObject.getBigDecimal("price");
                    BigDecimal servicePrice = feeObject.getBigDecimal("servicePrice");
                    BigDecimal totalFee = feeObject.getBigDecimal("totalFee");
                    BigDecimal serviceTotalFee = feeObject.getBigDecimal("serviceTotalFee");
                    BigDecimal differenceValue = feeObject.getBigDecimal("differenceValue");
                    if (!(differenceValue.signum() > 0)) {
                        // signum() 返回：1(正数), 0(零), -1(负数)
                        log.error("differenceValue 小于等于: 0,数据为：{}: " , differenceValue);
                        break;
                    }
                    //费用类型
                    Integer type = feeObject.getInteger("type");
                    this.insertRecord(dbDevice, dbDeviceThingsEnergy, thingsModelSimpleItem, thingsModelSimpleItem.getId(), price, servicePrice, totalFee, serviceTotalFee, differenceValue, dbDeviceThingsEnergy.getWorkshopId(), dbDeviceThingsEnergy.getBuildingName(), type);
                }

                break;
        }

    }


    /**
     * 高性能版本：预先计算时间段
     *
     * @param timeList     时间段列表
     * @param priceList    价格列表
     * @param latestRecord
     * @return 总费用
     */
    public JSONObject calculateFeeTime(List<PriceSchemeTime> timeList, JSONArray priceList, ThingsModelSimpleItem thingsModelSimpleItem, TDEnergyDeductionRecord latestRecord) {
        Date end = thingsModelSimpleItem.getTs();
        Integer typeByTime = getTypeByTime(timeList, end);
        if (typeByTime == null) {
            log.error("未找到对应的时间段");
            return null;
        }

        Date start = latestRecord.getTs();
        // 计算时长（小时）
        long durationMillis = end.getTime() - start.getTime();
        BigDecimal durationHours = new BigDecimal(durationMillis)
                .divide(new BigDecimal(3600000), 8, BigDecimal.ROUND_HALF_UP);

        // 获取价格并计算费用
        BigDecimal price = getPriceByType(typeByTime, priceList);
        BigDecimal servicePrice = getServicePriceByType(typeByTime, priceList);

        //计量，需要获取上次时间
        BigDecimal fee = price.multiply(durationHours);
        BigDecimal serviceFee = servicePrice.multiply(durationHours);

        return JSONObject.of("price", price, "servicePrice", servicePrice, "totalFee", fee, "serviceTotalFee", serviceFee)
                .fluentPut("differenceValue", durationHours)
                .fluentPut("type", typeByTime);

    }


    /**
     * 高性能版本：预先计算时间段
     *
     * @param timeList  时间段列表
     * @param priceList 价格列表
     * @return 总费用
     */
    public JSONObject calculateFeeValue(List<PriceSchemeTime> timeList, JSONArray priceList, ThingsModelSimpleItem thingsModelSimpleItem, TDEnergyDeductionRecord latestRecord) {
        Date end = thingsModelSimpleItem.getTs();
        Integer typeByTime = getTypeByTime(timeList, end);
        if (typeByTime == null) {
            log.error("未找到对应的时间段");
            return null;
        }

        // 计算用量
//        BigDecimal durationValue = new BigDecimal(thingsModelSimpleItem.getValue()).subtract(latestRecord.getCurrentValue());
        BigDecimal durationValue = new BigDecimal(thingsModelSimpleItem.getValue()).subtract(latestRecord.getLastValue());
//        BigDecimal durationValue = latestRecord.getCurrentValue().subtract(latestRecord.getLastValue());

        // 获取价格并计算费用
        BigDecimal price = getPriceByType(typeByTime, priceList);
        BigDecimal servicePrice = getServicePriceByType(typeByTime, priceList);

        //计量，需要获取上次时间
        BigDecimal fee = price.multiply(durationValue);
        BigDecimal serviceFee = servicePrice.multiply(durationValue);

        return JSONObject.of("price", price, "servicePrice", servicePrice, "totalFee", fee, "serviceTotalFee", serviceFee)
                .fluentPut("differenceValue", durationValue)
                .fluentPut("type", typeByTime);

    }

    /**
     *
     * @param dbDevice
     * @param dbDeviceThingsEnergy
     * @param thingsModelSimpleItem
     * @param identifier
     * @param price
     * @param servicePrice
     * @param totalFee
     * @param totalServiceTotalFee
     * @param differenceValue
     * @param workshopId
     * @param buildingName
     * @param type
     */
    private void insertRecord(Device dbDevice, DeviceThingsEnergy dbDeviceThingsEnergy, ThingsModelSimpleItem thingsModelSimpleItem, String identifier, BigDecimal price, BigDecimal servicePrice, BigDecimal totalFee, BigDecimal totalServiceTotalFee, BigDecimal differenceValue, Integer workshopId, String buildingName, Integer type) {
        TDEnergyDeductionRecord insertRecord = new TDEnergyDeductionRecord();
        // 上次的记录
        TDEnergyDeductionRecord theLatestRecord = energyDeductionRecordService.selectLatestRecord(dbDevice.getSerialNumber(), identifier);
        if (theLatestRecord == null) {
            theLatestRecord = new TDEnergyDeductionRecord();
            theLatestRecord.setCurrentValue(new BigDecimal(0));
        }

        Long energyType = dbDeviceThingsEnergy.getEnergyType();
        Date ts = thingsModelSimpleItem.getTs();

        insertRecord.setTs(ts);
        insertRecord.setTsLong(IdUtils.nextTaosNsId());
        //能源单价类
        insertRecord.setType(type);
        insertRecord.setDeviceId(dbDevice.getDeviceId());
        insertRecord.setDeviceName(dbDevice.getDeviceName());
        //能源类型
        insertRecord.setEnergyType(Math.toIntExact(energyType));
        insertRecord.setIdentity(identifier);
        insertRecord.setPrice(price);
        insertRecord.setServicePrice(servicePrice);

        insertRecord.setLastValue(theLatestRecord.getCurrentValue());
        insertRecord.setCurrentValue(new BigDecimal(thingsModelSimpleItem.getValue()));
        insertRecord.setDifferenceValue(differenceValue);

        insertRecord.setPriceAmount(totalFee);
        insertRecord.setServicePriceAmount(totalServiceTotalFee);
        insertRecord.setTotalAmount(totalFee.add(totalServiceTotalFee));
        insertRecord.setDeviceNumber(dbDevice.getSerialNumber());
        insertRecord.setBuildingId(Long.valueOf(workshopId));
        insertRecord.setBuildingName(buildingName);
        energyDeductionRecordService.insertRecord(insertRecord);
    }


    private BigDecimal getPriceByType(int type, JSONArray priceList) {
        if (type > 0 && type < priceList.size() && priceList.get(type) != null) {
            JSONObject jsonObject = priceList.getJSONObject(type);
            return jsonObject.getBigDecimal("price");
        }
        return BigDecimal.ZERO;
    }

    private BigDecimal getServicePriceByType(int type, JSONArray priceList) {
        if (type > 0 && type < priceList.size() && priceList.get(type) != null) {
            JSONObject jsonObject = priceList.getJSONObject(type);
            return jsonObject.getBigDecimal("servicePrice");
        }
        return BigDecimal.ZERO;
    }


    private Integer getTypeByTime(List<PriceSchemeTime> timeList, Date time) {
        LocalTime now = time.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
        for (PriceSchemeTime priceTime : timeList) {
            LocalTime startTime = LocalTime.parse(priceTime.getStartTime());
            LocalTime endTime = "24:00".equals(priceTime.getEndTime()) ? LocalTime.parse("23:59:59") : LocalTime.parse(priceTime.getEndTime());
            Integer priceType = priceTime.getType();
            boolean IsInCurrentTimePeriod = !now.isAfter(endTime) && !now.isBefore(startTime) && !now.equals(endTime);
            if (IsInCurrentTimePeriod) {
                return priceType;
            }
        }

        return null;
    }


}
